import React from 'react'
import { ActionManager } from '../actions/manager'
import { getNonDeletedElements } from '../element'
import { ExcalidrawElement, PointerType } from '../element/types'
import { t } from '/imports/i18n'
import { useDevice } from './ExcalidrawCore'
import {
  canChangeRoundness,
  canHaveArrowheads,
  getTargetElements,
  hasBackground,
  hasStrokeStyle,
  hasStrokeWidth,
  hasText
} from '../scene'
import { SHAPES } from '../shapes'
import { AppState, Zoom } from '../types'
import { capitalizeString, isTransparent, updateActiveTool, setCursorForShape } from '../utils'
import Stack from './Stack'
import { ToolButton } from './ToolButton'
import { hasStrokeColor } from '../scene/comparisons'
import { trackEvent } from '../analytics'
import { hasBoundTextElement } from '../element/typeChecks'
import clsx from 'clsx'
import { actionToggleZenMode } from '../actions'
import './Actions.style.scss'
import { Tooltip } from './Tooltip'
import { shouldAllowVerticalAlign } from '../element/textElement'

export const SelectedShapeActions = ({
  appState,
  elements,
  renderAction
}: {
  appState: AppState
  elements: readonly ExcalidrawElement[]
  renderAction: ActionManager['renderAction']
}) => {
  const targetElements = getTargetElements(getNonDeletedElements(elements), appState)

  let isSingleElementBoundContainer = false
  if (
    targetElements.length === 2 &&
    (hasBoundTextElement(targetElements[0]) || hasBoundTextElement(targetElements[1]))
  ) {
    isSingleElementBoundContainer = true
  }
  const isEditing = Boolean(appState.editingElement)
  const device = useDevice()
  const isRTL = document.documentElement.getAttribute('dir') === 'rtl'

  const showFillIcons =
    hasBackground(appState.activeTool.type) ||
    targetElements.some(
      element => hasBackground(element.type) && !isTransparent(element.backgroundColor)
    )
  const showChangeBackgroundIcons =
    hasBackground(appState.activeTool.type) ||
    targetElements.some(element => hasBackground(element.type))

  const showLinkIcon = targetElements.length === 1 || isSingleElementBoundContainer

  let commonSelectedType: string | null = targetElements[0]?.type || null

  for (const element of targetElements) {
    if (element.type !== commonSelectedType) {
      commonSelectedType = null
      break
    }
  }

  return (
    <div className='panelColumn'>
      <div>
        {((hasStrokeColor(appState.activeTool.type) &&
          appState.activeTool.type !== 'image' &&
          commonSelectedType !== 'image') ||
          targetElements.some(element => hasStrokeColor(element.type))) &&
          renderAction('changeStrokeColor')}
      </div>
      {showChangeBackgroundIcons && <div>{renderAction('changeBackgroundColor')}</div>}
      {showFillIcons && renderAction('changeFillStyle')}

      {(hasStrokeWidth(appState.activeTool.type) ||
        targetElements.some(element => hasStrokeWidth(element.type))) &&
        renderAction('changeStrokeWidth')}

      {(appState.activeTool.type === 'freedraw' ||
        targetElements.some(element => element.type === 'freedraw')) &&
        renderAction('changeStrokeShape')}

      {(hasStrokeStyle(appState.activeTool.type) ||
        targetElements.some(element => hasStrokeStyle(element.type))) && (
        <>
          {renderAction('changeStrokeStyle')}
          {renderAction('changeSloppiness')}
        </>
      )}

      {(canChangeRoundness(appState.activeTool.type) ||
        targetElements.some(element => canChangeRoundness(element.type))) && (
        <>{renderAction('changeRoundness')}</>
      )}

      {(hasText(appState.activeTool.type) ||
        targetElements.some(element => hasText(element.type))) && (
        <>
          {renderAction('changeFontSize')}

          {renderAction('changeFontFamily')}

          {renderAction('changeTextAlign')}
        </>
      )}

      {shouldAllowVerticalAlign(targetElements) && renderAction('changeVerticalAlign')}
      {(canHaveArrowheads(appState.activeTool.type) ||
        targetElements.some(element => canHaveArrowheads(element.type))) && (
        <>{renderAction('changeArrowhead')}</>
      )}

      {renderAction('changeOpacity')}

      <fieldset>
        <legend>{t('labels.layers')}</legend>
        <div className='buttonList'>
          {renderAction('sendToBack')}
          {renderAction('sendBackward')}
          {renderAction('bringToFront')}
          {renderAction('bringForward')}
        </div>
      </fieldset>

      {targetElements.length > 1 && !isSingleElementBoundContainer && (
        <fieldset>
          <legend>{t('labels.align')}</legend>
          <div className='buttonList'>
            {
              // swap this order for RTL so the button positions always match their action
              // (i.e. the leftmost button aligns left)
            }
            {isRTL ? (
              <>
                {renderAction('alignRight')}
                {renderAction('alignHorizontallyCentered')}
                {renderAction('alignLeft')}
              </>
            ) : (
              <>
                {renderAction('alignLeft')}
                {renderAction('alignHorizontallyCentered')}
                {renderAction('alignRight')}
              </>
            )}
            {targetElements.length > 2 && renderAction('distributeHorizontally')}
            {/* breaks the row ˇˇ */}
            <div style={{ flexBasis: '100%', height: 0 }} />
            <div
              style={{
                display: 'flex',
                flexWrap: 'wrap',
                gap: '.5rem',
                marginTop: '-0.5rem'
              }}
            >
              {renderAction('alignTop')}
              {renderAction('alignVerticallyCentered')}
              {renderAction('alignBottom')}
              {targetElements.length > 2 && renderAction('distributeVertically')}
            </div>
          </div>
        </fieldset>
      )}
      {!isEditing && targetElements.length > 0 && (
        <fieldset>
          <legend>{t('labels.actions')}</legend>
          <div className='buttonList'>
            {!device.isMobile && renderAction('duplicateSelection')}
            {!device.isMobile && renderAction('deleteSelectedElements')}
            {renderAction('group')}
            {renderAction('ungroup')}
            {showLinkIcon && renderAction('hyperlink')}
          </div>
        </fieldset>
      )}
    </div>
  )
}

export const ShapesSwitcher = ({
  canvas,
  activeTool,
  setAppState,
  onImageAction,
  appState
}: {
  canvas: HTMLCanvasElement | null
  activeTool: AppState['activeTool']
  setAppState: React.Component<any, AppState>['setState']
  onImageAction: (data: { pointerType: PointerType | null }) => void
  appState: AppState
}) => (
  <>
    {SHAPES.map(({ value, icon, key, numericKey, fillable }, _index) => {
      const label = t(`toolBar.${value}`)
      const letter = key && (typeof key === 'string' ? key : key[0])
      const shortcut = letter
        ? `${capitalizeString(letter)} ${t('helpDialog.or')} ${numericKey}`
        : `${numericKey}`
      return (
        <ToolButton
          className={clsx('Shape', { fillable })}
          key={value}
          type='radio'
          icon={icon}
          checked={activeTool.type === value}
          name='editor-current-shape'
          title={`${capitalizeString(label)} — ${shortcut}`}
          keyBindingLabel={numericKey}
          aria-label={capitalizeString(label)}
          aria-keyshortcuts={shortcut}
          data-testid={`toolbar-${value}`}
          onPointerDown={({ pointerType }) => {
            if (!appState.penDetected && pointerType === 'pen') {
              setAppState({
                penDetected: true,
                penMode: true
              })
            }
          }}
          onChange={({ pointerType }) => {
            if (appState.activeTool.type !== value) {
              trackEvent('toolbar', value, 'ui')
            }
            const nextActiveTool = updateActiveTool(appState, {
              type: value
            })
            setAppState({
              activeTool: nextActiveTool,
              multiElement: null,
              selectedElementIds: {}
            })
            setCursorForShape(canvas, {
              ...appState,
              activeTool: nextActiveTool
            })
            if (value === 'image') {
              onImageAction({ pointerType })
            }
          }}
        />
      )
    })}
  </>
)

export const ZoomActions = ({
  renderAction,
  zoom: _zoom
}: {
  renderAction: ActionManager['renderAction']
  zoom: Zoom
}) => (
  <Stack.Col gap={1} className='zoom-actions'>
    <Stack.Row align='center'>
      {renderAction('zoomOut')}
      {renderAction('resetZoom')}
      {renderAction('zoomIn')}
    </Stack.Row>
  </Stack.Col>
)

export const UndoRedoActions = ({
  renderAction,
  className
}: {
  renderAction: ActionManager['renderAction']
  className?: string
}) => (
  <div className={`undo-redo-buttons ${className}`}>
    <div className='undo-button-container'>
      <Tooltip label={t('buttons.undo')}>{renderAction('undo')}</Tooltip>
    </div>
    <div className='redo-button-container'>
      <Tooltip label={t('buttons.redo')}> {renderAction('redo')}</Tooltip>
    </div>
  </div>
)

export const ExitZenModeAction = ({
  actionManager,
  showExitZenModeBtn
}: {
  actionManager: ActionManager
  showExitZenModeBtn: boolean
}) => (
  <button
    className={clsx('disable-zen-mode', {
      'disable-zen-mode--visible': showExitZenModeBtn
    })}
    onClick={() => actionManager.executeAction(actionToggleZenMode)}
  >
    {t('buttons.exitZenMode')}
  </button>
)

export const FinalizeAction = ({
  renderAction,
  className
}: {
  renderAction: ActionManager['renderAction']
  className?: string
}) => (
  <div className={`finalize-button ${className}`}>
    {renderAction('finalize', { size: 'small' })}
  </div>
)
